package qc.module.platform.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import qc.common.core.enums.ResourceStatusFlagEnum;
import qc.common.core.unify.QCUnifyReturnValue;
import qc.module.platform.dto.project.ProjectDto;
import qc.module.platform.dto.project.ProjectQueryConditionDto;
import qc.module.platform.entity.*;
import qc.module.platform.mapper.ProjectMapper;
import qc.module.platform.repository.*;

import java.util.List;
import java.util.stream.Collectors;

/**
 * ProjectService
 *
 * @author QuCheng Tech
 * @create 2023/1/29
 */
@Service
public class ProjectService {
    private QcProjectRepository repository;
    private UserProjectService userProjectService;

    private QcDeptRepository qcDeptRepository;

    private QcRoleRepository qcRoleRepository;

    private QcMenusRepository qcMenusRepository;

    private DeptUserService deptUserService;
    private RoleMenuService roleMenuService;

    private RoleUserService roleUserService;
    private RoleResourceService roleResourceService;

    @Autowired
    public void setQcDeptRepository(QcDeptRepository qcDeptRepository) {
        this.qcDeptRepository = qcDeptRepository;
    }

    @Autowired
    public void setQcMenusRepository(QcMenusRepository qcMenusRepository) {
        this.qcMenusRepository = qcMenusRepository;
    }

    @Autowired
    public void setRoleMenuService(RoleMenuService roleMenuService) {
        this.roleMenuService = roleMenuService;
    }

    @Autowired
    public void setRoleResourceService(RoleResourceService roleResourceService) {
        this.roleResourceService = roleResourceService;
    }

    @Autowired
    public void setRoleUserService(RoleUserService roleUserService) {
        this.roleUserService = roleUserService;
    }

    @Autowired
    public void setDeptUserService(DeptUserService deptUserService) {
        this.deptUserService = deptUserService;
    }

    @Autowired
    public void setQcRoleRepository(QcRoleRepository qcRoleRepository) {
        this.qcRoleRepository = qcRoleRepository;
    }

    @Autowired
    public void setRepository(QcProjectRepository repository) {
        this.repository = repository;
    }

    @Autowired
    public void setUserProjectService(UserProjectService userProjectService) {
        this.userProjectService = userProjectService;
    }

    private QcDeptUserRepository qcDeptUserRepository;

    @Autowired
    public void setQcDeptUserRepository(QcDeptUserRepository qcDeptUserRepository) {
        this.qcDeptUserRepository = qcDeptUserRepository;
    }

    /**
     * 新增项目
     *
     * @param dto 项目信息
     * @return 成功返回null
     * @author QuCheng Tech
     * @since 2023/6/26
     */
    public String add(ProjectDto dto) {
        String result = validProjectDto(dto);
        if (result != null) {
            return result;
        }

        //判断项目编码是否已经存在
        if (hasCodeExist(dto.getCode()))
            return QCUnifyReturnValue.Warn("项目编码已存在");

        //DTO转换为Entity
        QcProject en = ProjectMapper.MAPPER.ProjectAddDtoToEntity(dto);

        if (repository.insert(en) < 0x1)
            return QCUnifyReturnValue.Warn("保存失败");

        return QCUnifyReturnValue.Success();
    }

    /**
     * 修改项目
     *
     * @param dto 项目信息
     * @return 成功返回null
     * @author QuCheng Tech
     * @since 2023/6/26
     */
    public String update(ProjectDto dto) {
        String result = validProjectDto(dto);
        if (result != null) {
            return result;
        }

        //修改的项目编码是否存在
        if (!hasCodeExist(dto.getCode()))
            return QCUnifyReturnValue.Warn("修改的项目编码不存在");

        //DTO转换为Entity
        QcProject en = ProjectMapper.MAPPER.ProjectDtoToEntity(dto);

        repository.updateById(en);

        return QCUnifyReturnValue.Success();
    }

    /**
     * 验证项目Dto
     *
     * @param dto 项目信息
     * @return 返回null表示没问题
     * @author QuCheng Tech
     * @since 2023/6/29
     */
    private String validProjectDto(ProjectDto dto) {
        //判空
        if (dto == null)
            return QCUnifyReturnValue.Warn("项目信息不能为空");
        if (StringUtils.isBlank(dto.getCode()))
            return QCUnifyReturnValue.Warn("项目编码不能为空");
        if (StringUtils.isBlank(dto.getName()))
            return QCUnifyReturnValue.Warn("项目名称不能为空");

        //判断项目名称是否存在
        if (hasNameExist(dto.getName(), dto.getCode()))
            return QCUnifyReturnValue.Warn("项目名称已经存在");

        return null;
    }

    /**
     * 删除项目
     *
     * @param projectcode   项目编码
     * @param isLogicDelete 是否为逻辑删除
     * @return 成功返回null
     * @author QuCheng Tech
     * @since 2023/6/27
     */
    public String delete(String projectcode, boolean isLogicDelete) {
        //1，判空
        if (StringUtils.isBlank(projectcode))
            return QCUnifyReturnValue.Warn("删除的项目编码不能为空");

        //2.判断项目编码是否存在
        if (!hasCodeExist(projectcode))
            return QCUnifyReturnValue.Warn("要删除的项目不存在");

        //3.判断是否为逻辑删除
        if (isLogicDelete) {
            //3.1 逻辑删除，修改状态标记为已删除，不删除记录
            // 修改项目的状态标记
            LambdaUpdateWrapper<QcProject> wrapper = new LambdaUpdateWrapper<>();
            wrapper.eq(QcProject::getCode, projectcode);
            wrapper.set(QcProject::getFlag, ResourceStatusFlagEnum.DELETED);
            repository.update(null, wrapper);
        } else {
            //3.2 物理删除，删除前删除关联关系（项目分组、项目配置、项目与用户关联信息）
            //删除步骤
            //项目中需同步删除的数据：项目配置分组、项目配置信息、部门、人员、角色、菜单
            //在一个项目中部门、人员、角色、菜单之间存在关联关系，需要同步删除关联关系
            //1.删除项目配置分组、项目配置信息
            //2.删除关联关系：对每个关联表中的数据需要同时使用2种类型的关联对象进行删除，如部门与人员关联关系，需要按部门进行删除和按人员进行删除关联关系
            //3.删除项目中的部门、人员、角色、菜单
            //4.删除项目

            //为统一进行判断和删除操作，开始时根据项目编码进行一次项目中的部门、人员、角色、菜单信息的查询，得到对应的ID集合
            //该项目下部门ID集合
            List<Integer> projectDeptIds = getProjectDeptIds(projectcode);
            //该项目下菜单ID集合
            List<Integer> projectMenuIds = getProjectMenuIds(projectcode);
            //该项目下角色ID集合
            List<Integer> projectRolesIds = getProjectRolesIds(projectcode);
            //获得该项目下用户ID集合
            List<Integer> projectUserIds = userProjectService.getByProjectCode(projectcode);

            //2删除关联关系：对每个关联表中的数据需要同时使用2种类型的关联对象进行删除，如部门与人员关联关系，需要按部门进行删除和按人员进行删除关联关系
            //2.1删除部门和用户关联表中的数据
            //按部门ID删除
            if (projectDeptIds != null && !projectDeptIds.isEmpty()) {
                deptUserService.deleteByDeptIds(projectDeptIds);
            }
            //按用户删除，已经根据部门删除用户与部门关联关系，不能根据用户ID直接删除关联关系--会导致删除其他项目中的部门与用户关联

            //2.2删除角色与用户的关联关系
            //按角色删除
            if (projectRolesIds != null && !projectRolesIds.isEmpty()) {
                roleUserService.deleteByRoleIds(projectRolesIds);
            }
            //按用户删除，已经根据角色删除用户与角色关联关系，不能根据用户ID直接删除关联关系--会导致删除其他项目中的角色与用户关联

            //2.3删除角色与菜单的关联关系
            //按角色删除
            if (projectRolesIds != null && !projectRolesIds.isEmpty()) {
                roleMenuService.deleteByRoleIds(projectRolesIds);
            }
            //按菜单删
            if (projectMenuIds != null && !projectMenuIds.isEmpty()) {
                roleMenuService.deleteByMenuIds(projectMenuIds);
            }
            //2.4删除项目与用户的关联关系
            userProjectService.deleteByProjectCode(projectcode);
            
            //3.删除项目中的部门 、角色、菜单
            //删除项目关联的部门
            deleteDeptsProjectCode(projectcode);

            //删除项目关联的菜单
            deleteMenusProjectCode(projectcode);

            //删除项目关联的角色
            deleteRolesProjectCode(projectcode);

            //4.删除项目
            repository.deleteById(projectcode);
        }

        return QCUnifyReturnValue.Success();
    }

    /**
     * 根据项目编码获取指定项目信息，不对项目的启用状态进行判断
     *
     * @param projectcode 项目编码
     * @return 项目信息
     * @author QuCheng Tech
     * @date 2023/1/29
     */
    public ProjectDto get(String projectcode) {
        return get(projectcode, false);
    }

    /**
     * 根据编码获取项目信息，可根据参数仅获取启用状态的
     *
     * @param projectcode 项目编码
     * @param isValid     是否仅为启用状态的
     * @return 项目信息
     * @author QuCheng Tech
     * @date 2023/1/29
     */
    public ProjectDto get(String projectcode, boolean isValid) {
        QcProject en = getEntity(projectcode, isValid);

        if (en != null)
            return ProjectMapper.MAPPER.toDto(en);

        return null;
    }

    /***
     * 获取指定的项目Entity，方便进行项目状态的判断
     *
     * @param projectCode 项目编码
     * @param isValid 是否有效，true表示项目必须为有效状态，false表示不管项目状态是否有效
     * @return qc.module.platform.entity.QcProject
     * @author QuCheng Tech
     * @since 2023/8/30
     */
    public QcProject getEntity(String projectCode, boolean isValid) {
        if (!StringUtils.isBlank(projectCode)) {
            LambdaQueryWrapper<QcProject> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(QcProject::getCode, projectCode);
            //是启用状态就查询状态为正常的，不是则不限状态
            if (isValid)
                wrapper.eq(QcProject::getFlag, ResourceStatusFlagEnum.NORMAL);

            return repository.selectOne(wrapper);
        }
        return null;
    }

    /**
     * 根据指定用户获取有效的项目列表，要求用户与项目关联信息存在、项目状态有效。
     *
     * @param userId 用户id
     * @return 返回指定用户关联的有效的项目列表信息
     * @author QuCheng Tech
     * @date 2023/6/29
     */
    public List<ProjectDto> getUserValidProjectList(Integer userId) {
        MPJLambdaWrapper<QcProject> qcProjectMPJLambdaWrapper = new MPJLambdaWrapper<QcProject>()
                .selectAll(QcProject.class)
                .innerJoin(QcUserProject.class, QcUserProject::getProjectcode, QcProject::getCode)
                .eq(QcUserProject::getUserid, userId)
                //查询有效项目
                .eq(QcProject::getFlag, ResourceStatusFlagEnum.NORMAL)
                .orderByAsc(QcProject::getName);
        List<ProjectDto> projectDtos = repository.selectJoinList(ProjectDto.class, qcProjectMPJLambdaWrapper);
        return projectDtos;
    }

    /**
     * 根据关键字查询，关键字可以为项目编码、项目名称、项目标题(完整、简要标题)，没有则查询全部
     *
     * @param dto ProjectQueryConditionDto
     * @return 项目信息集合
     * @author QcCheng Tech
     * @since 2023/2/8
     */
    public List<ProjectDto> query(ProjectQueryConditionDto dto, boolean isValid) {
        LambdaQueryWrapper<QcProject> wrapper = new LambdaQueryWrapper<>();

        if (dto != null) {
            if (!StringUtils.isBlank(dto.getWords())) {
                wrapper.like(QcProject::getCode, dto.getWords())
                        .or().like(QcProject::getName, dto.getWords());
            }
        }
        //根据项目编号降序排序
        wrapper.orderByDesc(QcProject::getCode);

        //是启用状态就查询状态为正常的，不是则不限状态
        if (isValid)
            wrapper.eq(QcProject::getFlag, ResourceStatusFlagEnum.NORMAL);

        List<QcProject> projectList = repository.selectList(wrapper);

        if (projectList.size() > 0x0)
            return ProjectMapper.MAPPER.toDtoList(projectList);

        return null;
    }

    /**
     * 判断指定的项目编码是否已经存在
     *
     * @param projectcode 项目编码
     * @return true表示已存在，false表示不存在
     * @author QuCheng Tech
     * @since 2023/6/26
     */
    public boolean hasCodeExist(String projectcode) {
        LambdaQueryWrapper<QcProject> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(QcProject::getCode, projectcode);

        QcProject en = repository.selectOne(wrapper);
        if (en != null)
            return true;

        return false;
    }

    /**
     * 获取项目信息
     *
     * @param flag: 项目状态
     * @return List<ProjectDto> 项目信息
     * @author QcCheng Tech
     * @date 2023/7/5
     */
    public List<ProjectDto> getValidProjectsList(ResourceStatusFlagEnum flag) {
        LambdaQueryWrapper<QcProject> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(QcProject::getFlag, flag);
        List<QcProject> en = repository.selectList(wrapper);
        if (en != null)
            return ProjectMapper.MAPPER.toDtoList(en);
        return null;
    }

    /**
     * 检查项目名称是否已经存在
     *
     * @param name        项目名称
     * @param projectcode 项目编码
     * @return true表示名称已存在，false表示名称不存在
     * @author QuCheng Tech
     * @since 2023/6/26
     */
    boolean hasNameExist(String name, String projectcode) {
        //验证名称是否重复条件：name=name and code<>code
        LambdaQueryWrapper<QcProject> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(QcProject::getName, name);
        wrapper.ne(QcProject::getCode, projectcode);

        QcProject en = repository.selectOne(wrapper);
        if (en != null)
            return true;

        return false;
    }

    /**
     * 获取指定项目关联的部门id集合
     *
     * @param projectCode 项目编码
     * @return 项目关联用户id集合
     * @author QcCheng Tech
     * @since 2023/6/28
     */
    public List<Integer> getProjectDeptIds(String projectCode) {
        //获得该项目下部门ID集合
        LambdaQueryWrapper<QcDepts> QcDeptWrapper = new LambdaQueryWrapper<>();
        QcDeptWrapper.select(QcDepts::getId);
        QcDeptWrapper.eq(QcDepts::getProjectcode, projectCode);
        List<QcDepts> qcDepts = qcDeptRepository.selectList(QcDeptWrapper);
        if (qcDepts != null && !qcDepts.isEmpty()) {
            return qcDepts.stream().map(QcDepts::getId).collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 获取指定项目关联的菜单id集合
     *
     * @param projectCode 项目编码
     * @return 项目关联菜单id集合
     * @author QcCheng Tech
     * @since 2023/6/28
     */
    public List<Integer> getProjectMenuIds(String projectCode) {
        //获得该项目下菜单ID集合
        LambdaQueryWrapper<QcMenus> QcMenusWrapper = new LambdaQueryWrapper<>();
        QcMenusWrapper.select(QcMenus::getId);
        QcMenusWrapper.eq(QcMenus::getProjectcode, projectCode);
        List<QcMenus> qcMenus = qcMenusRepository.selectList(QcMenusWrapper);
        if (qcMenus != null && !qcMenus.isEmpty()) {
            return qcMenus.stream().map(QcMenus::getId).collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 获取指定项目关联的角色id集合
     *
     * @param projectCode 项目编码
     * @return 项目关联用户id集合
     * @author QcCheng Tech
     * @since 2023/6/28
     */
    public List<Integer> getProjectRolesIds(String projectCode) {
        //获得该项目下角色ID集合
        LambdaQueryWrapper<QcRoles> QcRolesWrapper = new LambdaQueryWrapper<>();
        QcRolesWrapper.select(QcRoles::getId);
        QcRolesWrapper.eq(QcRoles::getProjectcode, projectCode);
        List<QcRoles> qcRoles = qcRoleRepository.selectList(QcRolesWrapper);
        if (qcRoles != null && !qcRoles.isEmpty()) {
            return qcRoles.stream().map(QcRoles::getId).collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 删除项目下的部门
     *
     * @param projectCode: 项目编码
     * @author QcCheng Tech
     * @date 2023/8/25
     */
    public void deleteDeptsProjectCode(String projectCode) {
        LambdaQueryWrapper<QcDepts> QcDeptWrapper = new LambdaQueryWrapper<>();
        QcDeptWrapper.select(QcDepts::getId);
        QcDeptWrapper.eq(QcDepts::getProjectcode, projectCode);
        qcDeptRepository.delete(QcDeptWrapper);
    }

    /**
     * 删除项目下的菜单
     *
     * @param projectCode: 项目编码
     * @author QcCheng Tech
     * @date 2023/8/25
     */
    public void deleteMenusProjectCode(String projectCode) {
        LambdaQueryWrapper<QcMenus> QcMenusWrapper = new LambdaQueryWrapper<>();
        QcMenusWrapper.select(QcMenus::getId);
        QcMenusWrapper.eq(QcMenus::getProjectcode, projectCode);
        qcMenusRepository.delete(QcMenusWrapper);
    }

    /**
     * 删除项目下的角色
     *
     * @param projectCode: 项目编码
     * @author QcCheng Tech
     * @date 2023/8/25
     */
    public void deleteRolesProjectCode(String projectCode) {
        LambdaQueryWrapper<QcRoles> QcRolesWrapper = new LambdaQueryWrapper<>();
        QcRolesWrapper.select(QcRoles::getId);
        QcRolesWrapper.eq(QcRoles::getProjectcode, projectCode);
        qcRoleRepository.delete(QcRolesWrapper);
    }

    /***
     * 设置指定项目编码的参数
     *
     * @param projectCode 项目编码
     * @param params 参数值，json字符串
     * @return java.lang.String
     * @author QuCheng Tech
     * @since 2025/2/3
     */
    public String setParams(String projectCode, String params) {
        LambdaUpdateWrapper<QcProject> wrapper = new LambdaUpdateWrapper<>();
        wrapper.set(QcProject::getParams, params);
        wrapper.eq(QcProject::getCode, projectCode);

        repository.update(null, wrapper);

        return QCUnifyReturnValue.Success();
    }
}
